﻿using System;
using System.Collections;
using System.Data.Services.Client;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Data.OData;

namespace Songhay.BiggestBox.Desktop.Modules.ODataWeather.Tests
{
    using ModelContext;
    using OData;

    /// <summary>
    /// Tests for OData access
    /// </summary>
    [TestClass]
    public class ODataTest
    {
        /// <summary>
        /// Gets or sets the test context.
        /// </summary>
        /// <value>
        /// The test context.
        /// </value>
        public TestContext TestContext { get; set; }

        /// <summary>
        /// OData test: should load weather.
        /// </summary>
        [TestCategory("Integration")]
        [TestMethod]
        public void ShouldLoadWeather()
        {
            var context = CambridgeWeatherContext.GetContext();
            var data = context.DataPoints
                .Where(i => i.TimePoint.Year == DateTime.Now.Year)
                .Where(i => i.TimePoint.Month == DateTime.Now.Month)
                .Select(i =>
                new
                {
                    DewPoint = i.DewPoint
                });
            data.ToList().ForEach(i => this.TestContext.WriteLine("DewPoint: {0}", i.DewPoint));
        }

        /// <summary>
        /// OData test: should load weather from asynchronous.
        /// </summary>
        /// <returns></returns>
        [TestCategory("Integration")]
        [TestMethod]
        public async Task ShouldLoadWeatherFromAsync()
        {
            var context = CambridgeWeatherContext.GetContext();

            var query = (DataServiceQuery)context.DataPoints
                .Where(i => i.TimePoint.Year == DateTime.Now.Year)
                .Where(i => i.TimePoint.Month == DateTime.Now.Month)
                .Select(i =>
                new
                {
                    DewPoint = i.DewPoint
                });

            this.TestContext.WriteLine("query Uri {0}", query.RequestUri.OriginalString);

            var result = await Task<IEnumerable>.Factory.FromAsync(query.BeginExecute, query.EndExecute, null);

            var boxList = result.OfType<object>().ToList();
            Assert.IsTrue(boxList.Any(), "The expected list of boxed objects is not here.");

            var data = boxList.Select(Mapper.DynamicMap<DataPoint>);
            Assert.IsTrue(data.Any(), "The expected list of auto-mapped objects is not here.");

            data.ToList().ForEach(i => this.TestContext.WriteLine("DewPoint: {0}", i.DewPoint));
        }

        /// <summary>
        /// OData test: should load weather with expected invalid operation exception from asynchronous.
        /// </summary>
        /// <returns></returns>
        [ExpectedException(typeof(InvalidOperationException))]
        [TestCategory("Integration")]
        [TestMethod]
        public async Task ShouldLoadWeatherWithExpectedInvalidOperationExceptionFromAsync()
        {
            var context = CambridgeWeatherContext.GetContext();

            var query = context.CreateQuery<DataPoint>(typeof(DataPoint).Name + "s")
                .AddQueryOption("TimePoint.Year", DateTime.Now.Year)
                .AddQueryOption("TimePoint.Month", DateTime.Now.Month);

            this.TestContext.WriteLine("query Uri {0}", query.RequestUri.OriginalString);

            var result = await Task<IEnumerable>.Factory.FromAsync(query.BeginExecute, query.EndExecute, null);

            try
            {
                var data = result.OfType<DataPoint>().ToList();
            }
            catch (InvalidOperationException expectedEx)
            {
                this.TestContext.WriteLine("InvalidOperationException: {0}", expectedEx.Message);

                var baseException = expectedEx.GetBaseException() as SystemException;
                if (baseException == null) this.TestContext.WriteLine("WARNING: the expected SystemException is not here.");

                this.TestContext.WriteLine("SystemException: {0}", baseException.Message);

                throw;
            }
        }

        /// <summary>
        /// OData test: should load weather with query option from asynchronous.
        /// </summary>
        /// <returns></returns>
        [TestCategory("Integration")]
        [TestMethod]
        public async Task ShouldLoadWeatherWithQueryOptionFromAsync()
        {
            var context = CambridgeWeatherContext.GetContext();

            //ref: http://www.odata.org/documentation/odata-version-2-0/uri-conventions/
            var query = context.CreateQuery<DataPoint>(typeof(DataPoint).Name + "s")
                .AddQueryOption("TimePoint.Year", DateTime.Now.Year)
                .AddQueryOption("TimePoint.Month", DateTime.Now.Month)
                .AddQueryOption("$select", "DewPoint,Humidity,Pressure,Rain,Sun,Temperature,TimePoint,WindDirection,WindSpeed,WindSpeedMax")
                .AddQueryOption("$orderby", "TimePoint desc")
                .AddQueryOption("$top", 10);

            this.TestContext.WriteLine("query Uri {0}", query.RequestUri.OriginalString);

            var result = await Task<IEnumerable>.Factory.FromAsync(query.BeginExecute, query.EndExecute, null);

            var data = result.OfType<DataPoint>().ToList();
            Assert.IsTrue(data.Any(), "The expected list of data points are not here.");

            data.ForEach(i => this.TestContext.WriteLine("TimePoint: {0}, Temperature: {1}, Humidity: {2}",
                i.TimePoint.ToShortDateString(),
                i.Temperature,
                i.Humidity));
        }
    }
}